#pragma once
#include <lcir/IR/Dialect.h>
#include <lcir/IR/ExprTemplate.h>
#include <lcir/IR/ExprTemps.h>
#include <lcir/IR/Expression.h>
#include <lcir/IR/LambdaType.h>
#include <lcir/IR/Op.h>
#include <lcir/IR/Prototype.h>
#include <lcir/IR/Type.h>

#include <Dialects/Builtin/Types.h>
#include <Dialects/Builtin/Prototype.h>
#include <Dialects/Builtin/Types.h>

namespace lcir::func {
class FuncDialect : public DialectCRTP<FuncDialect> {
 public:
  FuncDialect() : DialectCRTP<FuncDialect>("func") {}
};
// 模板
class FuncTemp : public ExprTempCRTP<FuncTemp> {
 public:
  FuncTemp(Expression *expr) : ExprTempCRTP(expr) {}
  Lambda *getFuncBody() { return expr->getOperandValue(0)->dyn_cast<Lambda>(); }
};

// 函数类型
class FuncTy : public VarComplexTypeCRTP<FuncTy, FuncDialect> {
  Type *retTy{nullptr};
  std::vector<Type *> paramTy;

 public:
  FuncTy() : VarComplexTypeCRTP("Func") {}
  FuncTy(const std::vector<Type *> &paramTy, Type *retTy) : FuncTy() {
    this->paramTy = paramTy;
    this->retTy = retTy;
  }
  Type *getResultType() { return retTy; }
};

// 函数原型
class FuncProto : public PrototypeCRTP<FuncProto, FuncDialect> {
 public:
  FuncProto(FuncTy *ty) : PrototypeCRTP("FuncProto", ty) {}
};

/**
 * @brief 创建一个函数
 */
class Func : public OpCRTP<Func, FuncDialect, FuncTemp, LiveNode> {
  std::string name{""};

 public:
  Func(std::string_view funcName) : OpCRTP("Func"), name(funcName) {}
  Func() : OpCRTP("Func") {}
  // operand为lambda
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    if (operandVals.size() != 1 || !operandVals[0]->getType()->isa<LambdaType>()) return false;
    return true;
  }

  // 类型推导
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    auto *ty = operandProtos[0]->getType()->dyn_cast<LambdaType>();
    auto *funcTy = FuncTy::get(ty->getParamTypes(), ty->getResultType());
    auto *ret = new FuncProto(funcTy);
    ret->setInfo(name);
    return {ret};
  }
};
/**
 * @param 函数参数分配，这里为了后续降级和寄存器分配，不返回指针，而是直接返回虚拟寄存器
 * 
 */
class ArgAlloca : public OpCRTP<ArgAlloca, FuncDialect> {
  std::string varName{""};
  Type *type{nullptr};

 public:
  ArgAlloca(Type *type) : OpCRTP("ArgAlloca"), type(type) {}
  ArgAlloca(Type *type, std::string_view varName) : OpCRTP("ArgAlloca"), varName(varName), type(type) {}
  bool checkType(const std::vector<Prototype *> &operandVals) override { return operandVals.empty(); }

  std::vector<Prototype *> deduceRetType(const std::vector<Prototype *> &operandProtos) override {
    Prototype *ret{nullptr};
    if (type->isa<builtin::Int32Type>()) {
      ret = new builtin::Int32VarProto();
    } else if (type->isa<builtin::FloatType>()) {
      ret = new builtin::FloatVarProto();
    } else if (auto ptr = type->dyn_cast<builtin::Pointer>()) {
      ret = new builtin::PointerVarProto(ptr->getElementType());
    } else {
      ret = new builtin::UnkownVarProto();
    }
    ret->setInfo(varName);
    return {ret};
  }
};

/**
 * @brief 函数参数赋值，不用指针，  左操作数是要赋的值 右操作数是目标的变量 (这个后面翻译成li)
 */
class ArgAssign : public OpCRTP<ArgAssign, FuncDialect, LiveNode> {
 public:
  ArgAssign() : OpCRTP("ArgAssign") {}
  bool checkType(const std::vector<Prototype *> &operandVals) override { return operandVals.size() == 2; }
  std::vector<Prototype *> deduceRetType(const std::vector<Prototype *> &operandProtos) override { return {}; }
};

class Return : public OpCRTP<Return, FuncDialect, LiveNode, Terminator> {
 public:
  std::string name{""};
  Return() : OpCRTP("Return") {}
  // operand为lambda
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override { return true; }

  // 类型推导
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return {};
  }
};
/**
 * @brief 函数调用
 */
class Call : public OpCRTP<Call, FuncDialect> {
  std::string name{""};

 public:
  Call() : OpCRTP("Func") {}
  // operand为lambda
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    if (!operandVals[0]->getType()->isa<FuncTy>()) return false;
    return true;
  }

  // 类型推导
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    auto funcTy = operandProtos[0]->getType()->dyn_cast<FuncTy>();
    if (funcTy->getResultType() == nullptr) return {};
    // TODO：看看返回值是int还是float
    if (funcTy->getResultType()->isa<builtin::Int32Type>()) return {new builtin::Int32VarProto()};
    if (funcTy->getResultType()->isa<builtin::FloatType>()) return {new builtin::FloatVarProto()};
    return {new builtin::UnkownVarProto()};
  }
};

}  // namespace lcir::func